#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include "utest.h"

typedef struct
{
    int id;
    char *desc;
    void (*fp)();
    int ok;
} utest_t;
int is_fail = 0;
static int utest_capacity;
static utest_t *utest_array;
static int utest_count;
static int utest_log;

void utest_add(char *desc, void (*fp)())
{
    assert(utest_count <= utest_capacity);

    if (utest_capacity == 0)
    {
        utest_capacity = 2;
        utest_array = malloc(utest_capacity * sizeof(utest_t));
    }

    if (utest_count == utest_capacity)
    {
        utest_capacity *= 2;
        utest_array = realloc(utest_array, utest_capacity * sizeof(utest_t));
    }

    utest_t *utest = utest_array + utest_count;
    utest->id = utest_count;
    utest->desc = strdup(desc);
    utest->fp = fp;
    utest_count++;
}

void utest_parse_args(int argc, char *argv[], char *target_arg, void (*fp)())
{
    for (int i = 0; i < argc; i++)
    {
        char *arg = argv[i];
        if (strcmp(arg, target_arg) == 0)
        {
            fp();
            exit(EXIT_SUCCESS);
        }
    }
}

void utest_exec(utest_t *utest)
{
    pid_t pid = fork();
    if (pid == 0)
    {
        alarm(3);
        utest->fp();
        exit(0);
    }
    int status;
    int error = waitpid(pid, &status, 0);
    if ((error > 0) && WIFEXITED(status) && (WEXITSTATUS(status) == 0))
    {
        printf("√\n");
        utest->ok = 1;
    }
    else
    {
        is_fail = 1;
        printf("x\n");
        utest->ok = 0;
    }
}

void utest_run(void)
{
    int i;
    int fd = open("utest.log", O_WRONLY | O_CREAT, 0666);
    if (fd < 0)
    {
        perror("open log");
        exit(EXIT_FAILURE);
    }
    dup2(fd, 2); // 将错误输出定向到指定文件中
    close(fd);
    setvbuf(stdout, NULL, _IONBF, 0); // 标准输出设置为无缓冲，不使用外部缓冲区
    for (i = 0; i < utest_count; i++)
    {
        printf("%03d: %-32s", utest_array[i].id, utest_array[i].desc);
        utest_exec(&utest_array[i]);
    }
    if (is_fail)
    {
        printf("\nThe following tests failed:\n");
        for (i = 0; i < utest_count; i++)
        {
            if (utest_array[i].ok == 0)
                printf("%03d: %s\n", utest_array[i].id, utest_array[i].desc);
        }
        printf("\nSee utest.log for details.\n");
    }
    else
    {
        printf("\nPass all the tests.\n");
    }
}
